#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>

#define DEFAULT 8081

class UdpServer
{
public:
  UdpServer(/*std::string ip,*/ int port = DEFAULT)
    :_port(port)
    ,_sockfd(-1)
    //,_ip(ip)
  {}
  void UdpServerInit()
  {
    // 创建套接字
    // 协议家族 AF_INET(16位)
    // 服务类型  TCP: SOCK_STREAM 流式服务   UDP: SOCK_DGRAM 数据报
    // 服务类别 0  根据前两个参数自动推导所需协议
    _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (_sockfd < 0){
      std::cerr << "socket creat fail" << std::endl;
      exit(-1);
    }
    std::cout << "socket creat success, socketfd: " << _sockfd << std::endl;
    
    // 绑定端口号 上面的步骤只是创建了一个文件，需要把文件和网络关联起来，这样才可以从网络中读取数据和把数据刷新到网卡中
    // 所以需要绑定端口号
    // sockaddr
    struct sockaddr_in local;
    
    memset(&local, '\0', sizeof(local));
    local.sin_family = AF_INET;
    local.sin_port = htons(_port); // 将主机序列的端口号转为网络序列的端口号
    //local.sin_addr.s_addr = inet_addr(_ip.c_str());// 将字符串ip转为整数ip
    local.sin_addr.s_addr = INADDR_ANY;// 取消ip绑定，可以接受来自任意client的请求，从任意ip获取数据
  
    if (bind(_sockfd, (sockaddr*)&local, sizeof(local)) == -1){
        std::cerr << "bind fail" << std::endl;
        exit(-1);
    }
    std::cout << "bind port success, port: " <<  _port << std::endl;
  }

  void UdpServerStart()
  {
    while (1){
      // recvfrom 读数据  flags 0非阻塞读取
      char buf[1024];
      struct sockaddr_in peer;// 获取远端数据和信息
      socklen_t len = sizeof(peer);// 输入输出型参数
      ssize_t size = recvfrom(_sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&peer, &len);
      if (strcmp("exit",buf)==0)
        continue;
      if (size > 0){
        buf[size] = 0;
        int peerPort = ntohs(peer.sin_port);
        std::string peerIp = inet_ntoa(peer.sin_addr);// 将网络序列整数的ip转为主机序列的字符串ip
        std::string result;
        if (strcmp(buf, "pwd") == 0
            ||strcmp(buf, "who am i") == 0
            ||strcmp(buf, "ls") == 0){
          // 创建管道
          int fd[2];
          pipe(fd);
          if (fork()==0){
            // child
            close(fd[0]);
            // dup2 重定向，把ls的结果重定向到管道中
            dup2(fd[1], 1);
            // 程序替换
            if (strcmp(buf, "pwd") == 0)
              execl("/usr/bin/pwd", "pwd", nullptr);
            else if (strcmp(buf, "who am i") == 0)
              execl("/usr/bin/who", "who am i", nullptr);
            else if (strcmp(buf, "ls") == 0)
              execl("/usr/bin/ls", "ls", "-l", "-i", nullptr);
          }

          // parent
          close(fd[1]);
          char ch;
          // 从管道读取数据
          while (read(fd[0], &ch, 1) > 0){
            result += ch;
          }
        }

        std::cout << peerIp << ":" << peerPort << "# " << buf << std::endl;
        
        std::string echo_msg;
        if (result.size() == 0){
          echo_msg = "server get!-> ";
          echo_msg += buf;
        }
        else{
          echo_msg += "server get!-> \n";
          echo_msg += result;
        }
        sendto(_sockfd, echo_msg.c_str(), echo_msg.size(), 0, (struct sockaddr*)&peer, len);
      }
      else{
        std::cerr << "recvfrom error" << std::endl;
        std::string error_msg = "server recvfrom error ! ! !";
        sendto(_sockfd, error_msg.c_str(), error_msg.size(), 0, (struct sockaddr*)&peer, len);
      }
    }
  }
  ~UdpServer()
  {
    if (_sockfd >= 0){
      close(_sockfd);
    }
  }
private:
  int _port;
  int _sockfd;
  //std::string _ip;// 可以去掉
};
